package com.tanguy.driver.utlis.extend

import android.annotation.SuppressLint
import android.content.Context
import android.provider.Settings
import android.util.Log
import cn.sinata.xldutils_kotlin.data.ResultData
import cn.sinata.xldutils_kotlin.rxutils.Error
import cn.sinata.xldutils_kotlin.rxutils.ResultException
import com.google.gson.JsonSyntaxException
import com.tanguy.driver.Const
import com.tanguy.driver.interfaces.RequestHelper
import com.weigan.loopview.LoopView
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.util.concurrent.TimeUnit


/**
 * Created by dab on 2017/11/2 0002 14:06
 * 一些公共方法
 */
/**
 * 打印日志,快速定位到日志的位置,会收集栈的信息
 */
fun loge(msg: Any? = null, stack: Int = 2, myTag: String = "tanguy_loge") {
    if (!Const.DEBUG.All) return
    val stackTrace = Throwable().stackTrace
    val className = stackTrace[stack].fileName
    val methodName = stackTrace[stack].methodName
    val lineNumber = stackTrace[stack].lineNumber
    val tag = "($className:$lineNumber)"
    val msg = ("$myTag $methodName:${msg.toString()}")
    Log.e(tag, msg)
}

/**
 * 计时器(注意不需要的时候要取消订阅)
 */
inline fun rxTimer(interval: Long, crossinline block: (times: Long) -> Unit): Disposable {
    var subscribe: Disposable? = null
    subscribe = Observable.interval(0, interval, TimeUnit.MILLISECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                block.invoke(it)
            }
    return subscribe
}
/**
 * 倒计时s
 */
inline fun countDownTimer(second: Long, crossinline block: (finish: Boolean, millisUntilFinished: Long) -> Unit): Disposable {
    var subscribe: Disposable? = null
    subscribe = Observable.interval(0, 1000, TimeUnit.MILLISECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                if (it < second) {
                    block.invoke(false, second - it)
                } else {
                    block.invoke(true, 0)
                    if (subscribe != null && !subscribe!!.isDisposed) {
                        subscribe?.dispose()
                    }
                }
            }
    return subscribe
}

/**
 * 验证码的倒计时
 */
inline fun captchaCountDownTimer(crossinline block: (finish: Boolean, msg: String) -> Unit): Disposable {
    return countDownTimer(60) { finish, millisUntilFinished ->
        if (finish) {
            block.invoke(finish, "重新获取验证码")
        } else {
            block.invoke(finish, "${millisUntilFinished}s")
        }
    }
}

/**
 * 获取设备id
 */
@SuppressLint("HardwareIds")
fun Context.getDeviceId(): String = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)

/**
 * 通过反射拿到LoopView控件当前选择的数据
 *
 * @param loopView
 * @return
 */
fun LoopView.getSelectedItemString(): String? {
    val aClass = this.javaClass
    val declaredFields = aClass.declaredFields
    for (field in declaredFields) {
        if (field.name == "items") {
            try {
                field.isAccessible = true
                val list = field.get(this) as List<String>
                return list[this.selectedItem]
            } catch (e: Exception) {
                e.printStackTrace()
            }

        }
    }
    return null
}


/**
 * 简化请求的一个方法(只有code==0时才算成功),直接在主线程返回,如果请求失败,会在这个里面提醒用户
 * 请求需要一个RequestHelper接口,因为要
 *     显示请求动画,
 *     取消动画,
 *     请求完成自动取消订阅,
 *     取消未完成时取消请求,
 *     错误提醒的弹窗提示
 *
 * 在调试的时候没有使用inline,因为内联会把回调内联进函数,日志不好记录堆栈的信息
 *
 */
fun <O, I : ResultData<O>> Observable<I>.requestSucceed(requestHelper: RequestHelper, showLoadDialog: Boolean = true, data: (data: O) -> Unit) {
    this.request(requestHelper, showLoadDialog) {
        if (it == null) return@request
        if (it.code == 0) {
            data.invoke(it.data as O)
        } else {
            requestHelper.showToast(it.msg)
        }
    }
}

/**
 * 返回所有的请求成功的数据,code!=0时也会返回
 * 如果需要code不为0时,不直接提示,而是需要处理的时候,使用这个
 */
fun <T> Observable<T>.request(requestHelper: RequestHelper, showLoadDialog: Boolean = true, data: (t: T?) -> Unit) {
    this
            .subscribeOn(Schedulers.io())
            .doOnSubscribe {
                if (showLoadDialog)
                    requestHelper.showLoadDialog()
            }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<T> {
                var disposable: Disposable? = null
                override fun onError(t: Throwable) {
                    requestHelper.dismissLoadDialog()
                    var msg = t.message
                    var code = -1
                    when (t) {
                        is JsonSyntaxException -> msg = Error.PARSER_ERROR
                        is ConnectException -> msg = Error.NET_ERROR
                        is SocketTimeoutException -> msg = Error.NET_ERROR
                        is HttpException -> msg = Error.SERVER_ERROR
                        is ResultException -> {
                            code = t.code
                            msg = t.message!!
                        }
                    }
                    data.invoke(null)
                    requestHelper.showToast(msg)
                    if (Const.DEBUG.RESPONSE)
                        t.printStackTrace()
                }

                override fun onSubscribe(d: Disposable) {
                    disposable = d
                    requestHelper.cancelRequest(d)
                }

                override fun onComplete() {
                    requestHelper.dismissLoadDialog()
                    if (disposable != null && !disposable!!.isDisposed) {
                        disposable?.dispose()
                    }
                }

                override fun onNext(t: T) {
                    requestHelper.dismissLoadDialog()
                    data.invoke(t)
                }

            })
}